pub mod storage;
pub mod manager;
pub mod types;
pub mod migration;
pub mod backup;
pub mod workspace;

#[cfg(test)]
mod tests;

pub use storage::*;
pub use manager::*;
pub use types::*;
pub use migration::*;
pub use backup::*;
pub use workspace::*;

use crate::{Result, CodeGraphError};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

/// Configuration system errors
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
    #[error("Configuration file not found: {0}")]
    FileNotFound(String),

    #[error("Invalid configuration format: {0}")]
    InvalidFormat(String),

    #[error("Configuration validation failed: {0}")]
    ValidationFailed(String),

    #[error("Version mismatch: expected {expected}, found {found}")]
    VersionMismatch { expected: String, found: String },

    #[error("Backup operation failed: {0}")]
    BackupFailed(String),

    #[error("Migration failed: {0}")]
    MigrationFailed(String),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),
}

impl From<ConfigError> for CodeGraphError {
    fn from(err: ConfigError) -> Self {
        CodeGraphError::config_error(err.to_string())
    }
}

/// Configuration change event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigChangeEvent {
    pub key: String,
    pub old_value: Option<serde_json::Value>,
    pub new_value: serde_json::Value,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

/// Configuration validation result
#[derive(Debug, Clone)]
pub struct ValidationResult {
    pub is_valid: bool,
    pub errors: Vec<String>,
    pub warnings: Vec<String>,
}

impl ValidationResult {
    pub fn valid() -> Self {
        Self {
            is_valid: true,
            errors: Vec::new(),
            warnings: Vec::new(),
        }
    }

    pub fn invalid(errors: Vec<String>) -> Self {
        Self {
            is_valid: false,
            errors,
            warnings: Vec::new(),
        }
    }

    pub fn with_warnings(mut self, warnings: Vec<String>) -> Self {
        self.warnings = warnings;
        self
    }
}

/// Configuration change listener trait
pub trait ConfigChangeListener: Send + Sync {
    fn on_config_changed(&self, event: &ConfigChangeEvent);
}

/// Configuration validator trait
pub trait ConfigValidator: Send + Sync {
    fn validate(&self, key: &str, value: &serde_json::Value) -> ValidationResult;
}

/// Default configuration values provider trait
pub trait DefaultConfigProvider: Send + Sync {
    fn get_defaults(&self) -> HashMap<String, serde_json::Value>;
}

/// Configuration schema definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigSchema {
    pub version: String,
    pub fields: HashMap<String, ConfigFieldSchema>,
}

/// Configuration field schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigFieldSchema {
    pub field_type: ConfigFieldType,
    pub default_value: Option<serde_json::Value>,
    pub required: bool,
    pub description: Option<String>,
    pub validation_rules: Vec<ValidationRule>,
}

/// Configuration field types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ConfigFieldType {
    String,
    Number,
    Boolean,
    Array,
    Object,
    Enum(Vec<String>),
}

/// Validation rules for configuration fields
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidationRule {
    MinLength(usize),
    MaxLength(usize),
    MinValue(f64),
    MaxValue(f64),
    Pattern(String),
    Required,
    Custom(String), // Custom validation function name
}

/// Configuration paths for different platforms
pub struct ConfigPaths;

impl ConfigPaths {
    /// Get the default configuration directory for the current platform
    pub fn config_dir() -> Result<PathBuf> {
        let config_dir = if cfg!(target_os = "windows") {
            dirs::config_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Windows config directory".to_string()))?
                .join("CodeGraph")
        } else if cfg!(target_os = "macos") {
            dirs::config_dir()
                .ok_or_else(|| ConfigError::FileNotFound("macOS config directory".to_string()))?
                .join("CodeGraph")
        } else {
            dirs::config_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Linux config directory".to_string()))?
                .join("codegraph")
        };

        Ok(config_dir)
    }

    /// Get the default data directory for the current platform
    pub fn data_dir() -> Result<PathBuf> {
        let data_dir = if cfg!(target_os = "windows") {
            dirs::data_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Windows data directory".to_string()))?
                .join("CodeGraph")
        } else if cfg!(target_os = "macos") {
            dirs::data_dir()
                .ok_or_else(|| ConfigError::FileNotFound("macOS data directory".to_string()))?
                .join("CodeGraph")
        } else {
            dirs::data_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Linux data directory".to_string()))?
                .join("codegraph")
        };

        Ok(data_dir)
    }

    /// Get the cache directory for the current platform
    pub fn cache_dir() -> Result<PathBuf> {
        let cache_dir = if cfg!(target_os = "windows") {
            dirs::cache_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Windows cache directory".to_string()))?
                .join("CodeGraph")
        } else if cfg!(target_os = "macos") {
            dirs::cache_dir()
                .ok_or_else(|| ConfigError::FileNotFound("macOS cache directory".to_string()))?
                .join("CodeGraph")
        } else {
            dirs::cache_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Linux cache directory".to_string()))?
                .join("codegraph")
        };

        Ok(cache_dir)
    }

    /// Get the logs directory for the current platform
    pub fn logs_dir() -> Result<PathBuf> {
        let logs_dir = if cfg!(target_os = "windows") {
            dirs::data_local_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Windows local data directory".to_string()))?
                .join("CodeGraph")
                .join("logs")
        } else if cfg!(target_os = "macos") {
            dirs::home_dir()
                .ok_or_else(|| ConfigError::FileNotFound("macOS home directory".to_string()))?
                .join("Library")
                .join("Logs")
                .join("CodeGraph")
        } else {
            dirs::cache_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Linux cache directory".to_string()))?
                .join("codegraph")
                .join("logs")
        };

        Ok(logs_dir)
    }

    /// Get the temporary directory for the current platform
    pub fn temp_dir() -> Result<PathBuf> {
        let temp_dir = std::env::temp_dir().join("codegraph");
        Ok(temp_dir)
    }

    /// Get the runtime directory for the current platform (for sockets, etc.)
    pub fn runtime_dir() -> Result<PathBuf> {
        let runtime_dir = if cfg!(target_os = "windows") {
            dirs::data_local_dir()
                .ok_or_else(|| ConfigError::FileNotFound("Windows local data directory".to_string()))?
                .join("CodeGraph")
                .join("runtime")
        } else if cfg!(target_os = "macos") {
            dirs::cache_dir()
                .ok_or_else(|| ConfigError::FileNotFound("macOS cache directory".to_string()))?
                .join("CodeGraph")
                .join("runtime")
        } else {
            // Try XDG_RUNTIME_DIR first, fallback to cache
            std::env::var("XDG_RUNTIME_DIR")
                .map(|dir| PathBuf::from(dir).join("codegraph"))
                .unwrap_or_else(|_| {
                    dirs::cache_dir()
                        .unwrap_or_else(|| PathBuf::from("/tmp"))
                        .join("codegraph")
                        .join("runtime")
                })
        };

        Ok(runtime_dir)
    }

    /// Normalize path separators for the current platform
    pub fn normalize_path(path: &PathBuf) -> PathBuf {
        if cfg!(target_os = "windows") {
            // Convert forward slashes to backslashes on Windows
            let path_str = path.to_string_lossy().replace('/', "\\");
            PathBuf::from(path_str)
        } else {
            // Convert backslashes to forward slashes on Unix-like systems
            let path_str = path.to_string_lossy().replace('\\', "/");
            PathBuf::from(path_str)
        }
    }

    /// Check if a path is absolute for the current platform
    pub fn is_absolute(path: &PathBuf) -> bool {
        path.is_absolute()
    }

    /// Convert a relative path to absolute based on a base directory
    pub fn to_absolute(path: &PathBuf, base_dir: &PathBuf) -> PathBuf {
        if path.is_absolute() {
            path.clone()
        } else {
            base_dir.join(path)
        }
    }

    /// Get the path separator for the current platform
    pub fn path_separator() -> &'static str {
        if cfg!(target_os = "windows") {
            "\\"
        } else {
            "/"
        }
    }

    /// Join paths using the correct separator for the current platform
    pub fn join_paths(base: &PathBuf, components: &[&str]) -> PathBuf {
        let mut result = base.clone();
        for component in components {
            result = result.join(component);
        }
        Self::normalize_path(&result)
    }
}

/// Utility functions for configuration management
pub mod utils {
    use super::*;
    use std::fs;

    /// Ensure directory exists, create if it doesn't
    pub fn ensure_dir_exists(path: &PathBuf) -> Result<()> {
        if !path.exists() {
            fs::create_dir_all(path)?;
        }
        Ok(())
    }

    /// Get file extension for configuration files
    pub fn config_file_extension() -> &'static str {
        "json"
    }

    /// Generate backup filename with timestamp
    pub fn generate_backup_filename(original: &str) -> String {
        let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
        format!("{}.backup.{}", original, timestamp)
    }

    /// Check if a configuration file is valid JSON
    pub fn is_valid_json_file(path: &PathBuf) -> bool {
        if let Ok(content) = fs::read_to_string(path) {
            serde_json::from_str::<serde_json::Value>(&content).is_ok()
        } else {
            false
        }
    }
}

/// Cross-platform configuration compatibility utilities
pub mod platform {
    use super::*;

    /// Platform-specific configuration adjustments
    pub struct PlatformConfig;

    impl PlatformConfig {
        /// Adjust configuration for the current platform
        pub fn adjust_for_platform(config: &mut AppConfig) -> Result<()> {
            // Adjust file paths
            Self::adjust_file_paths(config)?;

            // Adjust platform-specific settings
            Self::adjust_platform_settings(config)?;

            // Adjust performance settings based on platform
            Self::adjust_performance_settings(config)?;

            Ok(())
        }

        /// Adjust file paths for the current platform
        fn adjust_file_paths(config: &mut AppConfig) -> Result<()> {
            // Normalize backup directory path
            if let Some(backup_dir) = &mut config.files.backup.backup_dir {
                *backup_dir = ConfigPaths::normalize_path(backup_dir);
            }

            // Normalize extension directories
            for ext_dir in &mut config.extensions.extension_dirs {
                *ext_dir = ConfigPaths::normalize_path(ext_dir);
            }

            Ok(())
        }

        /// Adjust platform-specific settings
        fn adjust_platform_settings(config: &mut AppConfig) -> Result<()> {
            if cfg!(target_os = "windows") {
                // Windows-specific adjustments
                if config.editor.font_family == "monospace" {
                    config.editor.font_family = "Consolas".to_string();
                }

                // Adjust file associations for Windows
                config.files.associations.insert("*.bat".to_string(), "batch".to_string());
                config.files.associations.insert("*.cmd".to_string(), "batch".to_string());
                config.files.associations.insert("*.ps1".to_string(), "powershell".to_string());

            } else if cfg!(target_os = "macos") {
                // macOS-specific adjustments
                if config.editor.font_family == "monospace" {
                    config.editor.font_family = "SF Mono".to_string();
                }

                // Adjust file associations for macOS
                config.files.associations.insert("*.command".to_string(), "shell".to_string());

            } else {
                // Linux/Unix-specific adjustments
                if config.editor.font_family == "monospace" {
                    config.editor.font_family = "DejaVu Sans Mono".to_string();
                }

                // Adjust file associations for Linux
                config.files.associations.insert("*.desktop".to_string(), "desktop".to_string());
                config.files.associations.insert("*.appimage".to_string(), "executable".to_string());
            }

            Ok(())
        }

        /// Adjust performance settings based on platform capabilities
        fn adjust_performance_settings(config: &mut AppConfig) -> Result<()> {
            let cpu_count = num_cpus::get();

            // Adjust worker threads based on CPU count
            if config.performance.worker_threads == 0 {
                config.performance.worker_threads = (cpu_count / 2).max(1) as u32;
            }

            // Platform-specific memory adjustments
            if cfg!(target_os = "windows") {
                // Windows tends to have more available memory
                if config.performance.max_memory_mb < 512 {
                    config.performance.max_memory_mb = 1024;
                }
            } else if cfg!(target_os = "macos") {
                // macOS memory management is efficient
                if config.performance.cache_size_mb > 512 {
                    config.performance.cache_size_mb = 512;
                }
            } else {
                // Linux - conservative defaults
                if config.performance.max_memory_mb > 2048 {
                    config.performance.max_memory_mb = 1024;
                }
            }

            Ok(())
        }

        /// Get platform-specific default configuration
        pub fn get_platform_defaults() -> AppConfig {
            let mut config = AppConfig::default();

            // Apply platform adjustments
            if let Err(e) = Self::adjust_for_platform(&mut config) {
                eprintln!("Warning: Failed to apply platform adjustments: {}", e);
            }

            config
        }

        /// Check if configuration is compatible with current platform
        pub fn is_compatible(config: &AppConfig) -> PlatformCompatibilityResult {
            let mut result = PlatformCompatibilityResult {
                is_compatible: true,
                warnings: Vec::new(),
                recommendations: Vec::new(),
            };

            // Check font availability
            Self::check_font_compatibility(config, &mut result);

            // Check path compatibility
            Self::check_path_compatibility(config, &mut result);

            // Check performance settings
            Self::check_performance_compatibility(config, &mut result);

            result
        }

        fn check_font_compatibility(config: &AppConfig, result: &mut PlatformCompatibilityResult) {
            let common_fonts = if cfg!(target_os = "windows") {
                vec!["Consolas", "Courier New", "Lucida Console"]
            } else if cfg!(target_os = "macos") {
                vec!["SF Mono", "Monaco", "Menlo"]
            } else {
                vec!["DejaVu Sans Mono", "Liberation Mono", "Ubuntu Mono"]
            };

            if !common_fonts.contains(&config.editor.font_family.as_str()) {
                result.warnings.push(format!(
                    "Font '{}' may not be available on this platform",
                    config.editor.font_family
                ));
                result.recommendations.push(format!(
                    "Consider using one of these fonts: {}",
                    common_fonts.join(", ")
                ));
            }
        }

        fn check_path_compatibility(config: &AppConfig, result: &mut PlatformCompatibilityResult) {
            // Check backup directory
            if let Some(backup_dir) = &config.files.backup.backup_dir {
                if cfg!(target_os = "windows") && backup_dir.to_string_lossy().contains('/') {
                    result.warnings.push("Backup directory uses Unix-style path separators".to_string());
                    result.recommendations.push("Use Windows-style path separators (\\)".to_string());
                } else if !cfg!(target_os = "windows") && backup_dir.to_string_lossy().contains('\\') {
                    result.warnings.push("Backup directory uses Windows-style path separators".to_string());
                    result.recommendations.push("Use Unix-style path separators (/)".to_string());
                }
            }
        }

        fn check_performance_compatibility(config: &AppConfig, result: &mut PlatformCompatibilityResult) {
            let cpu_count = num_cpus::get();

            if config.performance.worker_threads > cpu_count as u32 * 2 {
                result.warnings.push("Worker thread count exceeds recommended maximum".to_string());
                result.recommendations.push(format!(
                    "Consider reducing worker threads to {} or less",
                    cpu_count * 2
                ));
            }

            // Platform-specific memory checks
            if cfg!(target_os = "windows") {
                if config.performance.max_memory_mb < 512 {
                    result.recommendations.push("Consider increasing memory limit on Windows".to_string());
                }
            }
        }
    }

    /// Platform compatibility check result
    #[derive(Debug, Clone)]
    pub struct PlatformCompatibilityResult {
        pub is_compatible: bool,
        pub warnings: Vec<String>,
        pub recommendations: Vec<String>,
    }
}
